home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / cenvid / cron.cmm < prev    next >
Encoding:
Text File  |  1995-10-20  |  18.8 KB  |  662 lines

  1. /*
  2.  * Cron.cmm
  3.  *
  4.  * This script is designed to be left running all the time. At times specified
  5.  * in the crontab file, other scripts and programs are run.
  6.  *
  7.  * CRONTAB format is each line as such:
  8.  *      MINUTE HOUR DAY_OF_MONTH MONTH DAY_OF_WEEK Command
  9.  *
  10.  * All fields are 0-based, except day of month. So the possible values are 0 to
  11.  * 1 less than you would expect. Sunday is 0 as is January.
  12.  *
  13.  * You can actually specify numbers in the field, or put in a * to match
  14.  * anything. You can use Words for month or day, use the first three letters.
  15.  * You can use numeric ranges or lists of numbers, such as:
  16.  *
  17.  * 1-4,5,6,8-10
  18.  *
  19.  *
  20.  * Ranges (and * which is consider min-max) can have a step, such as
  21.  * "* / 5" which means every five units. Spaces are not allowed.
  22.  *
  23.  * The day of month and day of week will both set off a match, so the entry
  24.  * will be run if EITHER matches. Use a dash in either field to match nothing.
  25.  *
  26.  *
  27.  * This version is designed to work on any CEnvi platform. If you come across
  28.  * something that does not work, please report it to us.
  29.  *
  30.  * Also note that this program uses some of the .CMM scripts distributed with
  31.  * CEnvi. If it cannot find them, some file types will not work. Currently
  32.  * supported file types and systems are:
  33.  *
  34.  *
  35.  * .NCF, .NLM           Netware
  36.  * .BAT                 All (Netware uses a batch interpreter script)
  37.  * .CMD                 OS/2
  38.  * .COM,.EXE            All except Netware
  39.  * .CMM                 All
  40.  *
  41.  * Any other extension will be done in the most generic way for that system.
  42.  * Hopefully, it will work.
  43.  *
  44.  * If you preceed the particular command with a '=', it is done synchronously
  45.  * as appropriate, usually on the same screen as cron. Don't let such
  46.  * applications hang or ask for input, because it is unlikely anyone will
  47.  * be around to do it. In this case, cron will be stuck waiting.
  48.  *
  49.  * The default is to launch the job asynchronously.
  50.  */
  51.  
  52. // The cron.tab's default location
  53. if( defined(_NWNLM_) )
  54.   {
  55.     crontab = "sys:/cron.tab";
  56.   } else {
  57.     crontab = "c:\\cron.tab";
  58.   }
  59.  
  60. seconds = 1;
  61.  
  62. // The last datestamp of the cron.tab file
  63. last_time = 0;
  64.  
  65. /* ---------------------------------------------------------------------- */
  66.  
  67. /*   Here is a C definition of the structure we are using, for easy of
  68.  *   modification
  69.  *
  70.  *   struct cron_entry {
  71.  *     char *command;
  72.  *     BYTE minute[60];           // We use flags for each of these fields
  73.  *     BYTE hour[24];
  74.  *     BYTE day[31];
  75.  *     BYTE month[12];
  76.  *     BYTE weekday[7];
  77.  *   };
  78.  */
  79.  
  80. /* ---------------------------------------------------------------------- */
  81.  
  82. /*
  83.  * Process a single numeric field, filling in the entries of the array
  84.  * to match. Return 0 on success, 1 on failure. Eat up all white space
  85.  * after the field.
  86.  */
  87. process_field(line,entry_array,max,text_strings,offset)
  88. {
  89. // By default, nothing is turned on.
  90.   for( i=0;i<max;i++ ) entry_array[i] = 0;
  91.  
  92.   comma_ok = 0;
  93.   while( line[0] && !isspace(line[0]) )
  94.     {
  95.       if( comma_ok==1 && line[0]==',' ) line++;
  96.       comma_ok = 1;
  97.  
  98.       if( line[0]=='-' ) { line++; continue; }
  99.       if( isdigit(line[0]) || line[0]=='*' )
  100.         {
  101.           first = 0; end = max-1; step = 1;
  102.  
  103. // First get a possible range
  104.           if( isdigit(line[0]) )
  105.             {
  106.               while( isdigit(line[0]) )
  107.                 { first = 10*first + line[0]-'0'; line++; }
  108.  
  109.               if( line[0]=='-' )
  110.                 {
  111.                   line++;
  112.                   end = 0;
  113.                   while( isdigit(line[0]) )
  114.                     { end = 10*end + line[0]-'0'; line++; }
  115.                 } else end = first;
  116.             } 
  117.           else line++;
  118.  
  119.           if( end<offset || end>=max+offset )
  120.             {
  121.               printf("Value is out of the valid range of %d-%d.\n",offset,max-1+offset);
  122.               return 1;
  123.             }
  124.  
  125. // And there may be a step value
  126.           if( line[0]=='/' )
  127.             {
  128.               line++;
  129.               step = 0;
  130.               while( isdigit(line[0]) )
  131.                 { step = 10*step + line[0]-'0'; line++; }
  132.             }
  133.  
  134. // Finally, set all those entries on.
  135.           for( i=first;i<=end;i+=step ) entry_array[i-offset] = 1;
  136.         } else {
  137.           if( text_strings==NULL || !isalpha(line[0]) ) return 1;
  138.           for( i=0;i<=GetArraySpan(text_strings);i++ )
  139.             if( !strnicmp(text_strings[i],line,3) )
  140.               {
  141.                 entry_array[i-offset] = 1;
  142.  
  143.                 line+=3; break;
  144.               }
  145.           if( i>GetArraySpan(text_strings) )
  146.             {
  147.               printf("Unrecognized text name for this field.\n");
  148.               return 1;
  149.             }
  150.         }
  151.     }
  152.  
  153.   while( isspace(line[0]) ) line++;
  154.  
  155.   return 0;
  156. }
  157.  
  158.  
  159. months =  
  160.   { "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec" };
  161. days =
  162.   { "sun","mon","tue","wed","thu","fri","sat" };
  163.  
  164. /*
  165.  * Process a single line from the cron.tab file and return an entry structure
  166.  * describing it. If the line is blank, a comment, or illegal return NULL;
  167.  */
  168. process_cron_line(line,line_number)
  169. {
  170.   while( isspace(line[0]) ) line++;
  171.  
  172. // Ignore comments or blank lines.
  173.   if( line[0]=='\n' || line[0]=='\0' || line[0]=='#' ) return NULL;
  174.  
  175. // The line's format is described in the comments at the top of this file.
  176.   if( process_field(line,entry.minute,60,NULL,0) )
  177.     { printf("Illegal entry field 1 on line %d.\n",line_number); return NULL; }
  178.   if( process_field(line,entry.hour,24,NULL,0) )
  179.     { printf("Illegal entry field 2 on line %d.\n",line_number); return NULL; }
  180.   if( process_field(line,entry.day,32,NULL,1) )
  181.     { printf("Illegal entry field 3 on line %d.\n",line_number); return NULL; }
  182.   if( process_field(line,entry.month,12,months,0) )
  183.     { printf("Illegal entry field 4 on line %d.\n",line_number); return NULL; }
  184.   if( process_field(line,entry.weekday,7,days,0) )
  185.     { printf("Illegal entry field 5 on line %d.\n",line_number); return NULL; }
  186.  
  187.   strcpy(entry.command,line);
  188.   s = strlen(entry.command) - 1;
  189.   if( entry.command[s]=='\n' ) entry.command[s] = '\0';
  190.  
  191.   return entry;
  192. }
  193.  
  194. /* ---------------------------------------------------------------------- */
  195.  
  196. print_at_then_return(col,row,text)
  197. {
  198.   pos = ScreenCursor();
  199.   ScreenCursor(col,row);
  200.   printf("%s",text); fflush(stdout);
  201.   ScreenCursor(pos.col,pos.row);
  202. }
  203.  
  204. /* ---------------------------------------------------------------------- */
  205.  
  206. /*
  207.  * Read the cron.tab file in, creating a cron_entry structure. This structure
  208.  * is returned. Return NULL if unable to read in the cron.tab or if the
  209.  * cron.tab is empty.
  210.  */
  211. read_crontab()
  212. {
  213.   sprintf(buffer,"Reading CRONTAB...",timebuf);
  214.   print_at_then_return(60,0,buffer);
  215.   Undefine(entries); entry_count = 0; line_number = 0;
  216.   buffer = ""; SetArraySpan(buffer,512);
  217.  
  218.   if( (fp = fopen(crontab,"r"))==NULL )
  219.     {
  220.       printf("Could not open file \"%s\".\n",crontab);
  221.       return NULL;
  222.     }
  223.   while( !feof(fp) )
  224.     {
  225.       line_number++;
  226.       if( fgets(buffer,512,fp)==NULL ) break;
  227.       entry = process_cron_line(buffer,line_number);
  228.       if( entry!=NULL ) entries[entry_count++] = entry;
  229.     }
  230.   fclose(fp);
  231.  
  232.   file = Directory(crontab);
  233.   if( file==NULL )
  234.     {
  235.       printf("Unable to get timestamp from crontab file.\n");
  236.       return NULL;
  237.     }
  238.   last_time = file[0].Write;
  239.  
  240.   return defined(entries)?entries:NULL;
  241. }
  242.  
  243.  
  244. /*
  245.  * The particular entry should be executed now using the system-dependant
  246.  * way of executing them. The file's type is checked and used.
  247.  */
  248. cron_execute(command)
  249. {
  250.   exec = "";
  251. // For cool printing of date-time.
  252.   timebuf = ""; SetArraySpan(timebuf,256);
  253.   strftime(timebuf,256,"%m/%d/%y %H:%M:%S",localtime(time()));
  254.  
  255.  
  256.   sprintf(buffer2,"LAST CRON: [%s] %s",timebuf,command);
  257.   sprintf(buffer,"%-80s",buffer2);
  258.   print_at_then_return(0,2,buffer);
  259.  
  260.   sync = 0;
  261.   if( command[0]=='=' ) { command++; sync = 1; }
  262.  
  263.   if(  (end = strchr(command,' '))==NULL )
  264.     end = command + strlen(command) - 4;
  265.   else
  266.     end -= 4;
  267.  
  268. //
  269. // Each type of executable that we know about is done for every system as
  270. // well as we can.
  271. //
  272.   if( defined(_NWNLM_) && !strnicmp(end,".NCF",4) )
  273.     {
  274.       system(command);
  275.       return;
  276.     }
  277.  
  278.   if( defined(_NWNLM_) && !strnicmp(end,".NLM",4) )
  279.     {
  280.       spawn(P_NOWAIT,command);
  281.       return;
  282.     }
  283.  
  284.   if( !strnicmp(end,".CMM",4) )
  285.     {
  286.       if( sync )
  287.         {
  288.           Interpret(command,INTERP_FILE|INTERP_NOINHERIT_LOCAL|
  289.                     INTERP_NOINHERIT_GLOBAL);
  290.           return;
  291.         }
  292.       if( defined(_NWNLM_) )
  293.         {
  294.           sprintf(exec,"load cenvi %s",command);
  295.           system(exec);
  296.           return;
  297.         }
  298.       if( defined(_WINDOWS_) )
  299.         {
  300.           sprintf(exec,"cenviw %s",command);
  301.           spawn(P_NOWAIT,exec);
  302.           return;
  303.         }
  304.       if( defined(_DOS_) )
  305.         {
  306.           sprintf(exec,"cenvid %s",command);
  307.           spawn(P_SWAP,exec);
  308.           return;
  309.         }
  310.       if( defined(_DOS32_) )
  311.         {
  312.           sprintf(exec,"cenvid32 %s",command);
  313.           spawn(P_NOWAIT,exec);
  314.           return;
  315.         }
  316.       if( defined(_OS2_) )
  317.         {
  318.           sprintf(exec,"cenvi2 %s",command);
  319.           spawn(P_NOWAIT,exec);
  320.           return;
  321.         }
  322.       if( defined(_NTCON_) )
  323.         {
  324.           sprintf(exec,"cenvint %s",command);
  325.           spawn(P_NOWAIT,exec);
  326.           return;
  327.         }
  328.       if( defined(_NTWIN_) )
  329.         {
  330.           sprintf(exec,"cenviwnt %s",command);
  331.           spawn(P_NOWAIT,exec);
  332.           return;
  333.         }
  334.  
  335. // Hmmm, an unrecognized version of CEnvi. Well, this is the most generic
  336. // way I can launch such a script.
  337.       sprintf(exec,"cenvi %s",command);
  338.       spawn(P_NOWAIT,exec);
  339.       return;
  340.     }
  341.  
  342.   if( !defined(_NWNLM_) && (!strnicmp(end,".EXE",4) || !strnicmp(end,".COM",4)) )
  343.     {
  344.       spawn(sync?P_WAIT:P_NOWAIT,command);
  345.       return;
  346.     }
  347.  
  348.   if( defined(_OS2_) && !strnicmp(end,".CMD",4) )
  349.     {
  350.       spawn(sync?P_WAIT:P_NOWAIT,command);
  351.       return;
  352.     }
  353.  
  354.   if( !strnicmp(end,".BAT",4) )
  355.     {
  356.       if( defined(_NWNLM_) )
  357.         {
  358.           if( sync )
  359.             {
  360.               sprintf(exec,"batch %s",command);
  361.               Interpret(exec,INTERP_FILE|INTERP_NOINHERIT_LOCAL|
  362.                         INTERP_NOINHERIT_GLOBAL);
  363.             } else {
  364.               sprintf(exec,"load cenvi batch %s",command);
  365.               spawn(P_NOWAIT,exec);
  366.             }
  367.           return;
  368.         }
  369.       if( defined(_DOS_) )
  370.         spawn(sync?P_WAIT:P_SWAP,command);
  371.       else
  372.         spawn(sync?P_WAIT:P_NOWAIT,command);
  373.       return;
  374.     }
  375.  
  376.   if( defined(_NWNLM_) )
  377.     {
  378.       spawn(P_NOWAIT,command);
  379.       return;
  380.     }
  381.  
  382.    
  383. if ( !strnicmp("start ",command,6) )
  384.   sprintf(exec,"%s",command);
  385. else
  386.   sprintf(exec,"start %s",command);
  387.   system(exec);
  388. }
  389.  
  390.  
  391. /*
  392.  * Go through each entry and see if it should be executed at this time.
  393.  */
  394. cron_checks(entries)
  395. {
  396.   current_time = localtime(time());
  397.   doit = 0;
  398.  
  399.   for( i=0;entries && i<=GetArraySpan(entries);i++ )
  400.     {
  401.       if( entries[i].minute[current_time.tm_min]  &&
  402.           entries[i].hour[current_time.tm_hour] &&
  403.           entries[i].month[current_time.tm_mon]  &&
  404. // Remember, the two day fields both can match
  405.           (entries[i].day[current_time.tm_mday-1]  ||
  406.            entries[i].weekday[current_time.tm_wday] )
  407.         )
  408. // Needed to stop CEnvi from passing the variable by reference.
  409.         {
  410.           cron_execute(=entries[i].command);
  411.           doit = 1;
  412.         }
  413.     }
  414.  
  415.   if( doit) update_list(entries);
  416. }
  417.  
  418.  
  419. /*
  420.  * Check if the crontab file has been changed
  421.  */
  422. cron_changed()
  423. {
  424.   file = Directory(crontab);
  425.   if( file==NULL ) return 0;
  426.  
  427.   return last_time!=file[0].Write;
  428. }
  429.  
  430.  
  431. /*
  432.  * Keeps a running clock on the screen.
  433.  */
  434. update_display()
  435. {
  436.   timebuf = ""; SetArraySpan(timebuf,256);
  437.   strftime(timebuf,256,"%m/%d/%y %H:%M:%S",localtime(time()));
  438.  
  439.  
  440.   sprintf(buffer,"%18s",timebuf);
  441.   print_at_then_return(60,0,buffer);
  442. }
  443.  
  444. /* ---------------------------------------------------------------------- */
  445.  
  446. num_days = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  447.  
  448.  
  449. /*
  450.  * Given an entry, figure out the next time it will be run. Return a structure
  451.  * describing that time (something to be printed). Return NULL if it
  452.  * will never again be run.
  453.  */
  454. next_time(entry)
  455. {
  456.   current_time = localtime(time());
  457.  
  458.  
  459. // first, determine the next month it is going to happen. It could either
  460. // be this month from now to the end of the month, or one of the next months
  461. // any time during the month. If we wrap around months, we must also increment
  462. // the year.
  463.  
  464. // Note, 0 and 13 are both this month. However, 0 is limited by this day & time
  465. // forward only.
  466.   for( month=0;month<13;month++ )
  467.     {
  468.       try_month = (current_time.tm_mon+month)%12;
  469. // We wrapped into next year.
  470.       if( month+current_time.tm_mon==12 ) current_time.tm_year++;
  471. // First, if cannot execute this month, we continue;
  472.       if( entry.month[try_month]==0 ) continue;
  473.  
  474.  
  475. // We find the next day that matched. Note, if the month==0, we do no wrapping
  476.       start_day = (month==0)?(current_time.tm_mday-1):0;
  477. // We ignore leap years, tough.
  478.       for( day=start_day;day<num_days[try_month];day++ )
  479.         {
  480.           tmp.tm_sec = 0; tmp.tm_min = 0; tmp.tm_hour = 0;
  481.           tmp.tm_mon = try_month; tmp.tm_mday = day+1;
  482. // rough approximation of DST
  483.           tmp.tm_isdst = (try_month>2 && try_month<10)
  484.           tmp.tm_year = current_time.tm_year;
  485.  
  486.           new = localtime(mktime(tmp));
  487.  
  488.           if( (entry.day[day]==0) && (entry.weekday[new.tm_wday]==0) )
  489.             continue;
  490.  
  491. // Ok, this month and day is the next possible choice. Let's find a time that will
  492. // work. Again, if month==0, it must be later than now.
  493.           start_hour = 0;
  494.           if( month==0 && day==start_day ) start_hour = current_time.tm_hour;
  495.           for( hour=start_hour;hour<24;hour++ )
  496.             {
  497.               if( entry.hour[hour]==0 ) continue;
  498.  
  499. // Finally, the minute: there must be some better way to do all this...
  500.               start_min = 0;
  501. // Start one minute after now - the 'now' minute has already been done.
  502.               if( month==0 && day==start_day && hour==start_hour )
  503.                 start_min = current_time.tm_min+1;
  504.               for( minute = start_min;minute<60;minute++ )
  505.                 {
  506.                   if( entry.minute[minute] )
  507.                     {
  508. // FOUND IT!!!!
  509.                       current_time.tm_mon = try_month;
  510.                       current_time.tm_mday = day+1;
  511.                       current_time.tm_hour = hour;
  512.                       current_time.tm_min = minute;
  513.                       return current_time;
  514.                     }
  515.                 }
  516.             }
  517.         }
  518.     }
  519.  
  520.  
  521. // We couldn't find a match, return never execute again. With the entry format
  522. // I believe this really means it can never execute period.
  523.   return NULL;
  524. }
  525.  
  526.  
  527.  
  528. datesort(elem1,elem2)
  529. {
  530.   if( elem1.tm_year<elem2.tm_year ) return -1;
  531.   if( elem1.tm_year>elem2.tm_year ) return 1;
  532.   if( elem1.tm_mon<elem2.tm_mon ) return -1;
  533.   if( elem1.tm_mon>elem2.tm_mon ) return 1;
  534.   if( elem1.tm_mday<elem2.tm_mday ) return -1;
  535.   if( elem1.tm_mday>elem2.tm_mday ) return 1;
  536.   if( elem1.tm_hour<elem2.tm_hour ) return -1;
  537.   if( elem1.tm_hour>elem2.tm_hour ) return 1;
  538.   if( elem1.tm_min<elem2.tm_min ) return -1;
  539.   if( elem1.tm_min>elem2.tm_min ) return 1;
  540.   return 0;
  541. }
  542.  
  543.  
  544.  
  545. /*
  546.  * Keep a list of all upcoming events on the screen
  547.  */
  548. update_list(entries)
  549. {
  550.   ScreenCursor(1,7); printf("Upcoming Events:\n");
  551.  
  552.   j = 0;
  553.  
  554. // First we build a table of the next time these entries will be going off
  555. // along with a text representation of such.
  556.   for( i=0;entries && i<=GetArraySpan(entries);i++ )
  557.     {
  558.       ret = next_time(entries[i]);
  559.       if( ret!=NULL )
  560.         {
  561. // rough approximation of DST
  562.           ret.tm_isdst = (ret.tm_mon>2 && ret.tm_mon<10)
  563.           next[j] = ret = localtime(mktime(ret));
  564.           next[j++].command = entries[i].command;
  565.         }
  566.     }
  567.   qsort(next,"datesort");
  568.  
  569.   for( i=0;i<10;i++ )
  570.     {
  571.       ScreenCursor(0,9+i);
  572.       if( i<j )
  573.         {
  574.           strftime(buf,"%a, %b %d, %Y at %I:%M %p   ",next[i]);
  575.           strcat(buf,next[i].command);
  576.           printf("%-79s\n",buf);
  577.         } else {
  578.           printf("                                                                               \n");
  579.         }
  580.     }
  581.  
  582.  
  583.   ScreenCursor(0,19);
  584.   printf("-------------------------------------------------------------------------------\n");
  585. }
  586.  
  587. /* ---------------------------------------------------------------------- */
  588.  
  589. /*
  590.  * Edit the crontab file using an appropriate editor for the system.
  591.  */
  592. invoke_editor()
  593. {
  594.   the_editor = "notepad";
  595.   if( defined(_DOS_) || defined(_DOS32_) )
  596.     {
  597.       mode = P_SWAP; the_editor = "edit";
  598.     } else
  599.       mode = P_NOWAIT;
  600.  
  601.   if( defined(_OS2_) ) the_editor = "e";
  602.   if( defined(_NWNLM_) ) the_editor = "edit";
  603.   if( defined(EDITOR) ) the_editor = EDITOR;
  604.   spawn(mode,the_editor,crontab);
  605. }
  606.  
  607. /* ---------------------------------------------------------------------- */
  608.  
  609. main(argc,argv)
  610. {
  611.   ScreenClear();
  612.   printf("CMM Cron, Version 1.1.\n");
  613.   printf("  [Q] Quit  [E] Edit Crontab\n\n");
  614.  
  615.  
  616. // You can specify the crontab as the first argument.
  617.   if( argc>=2 ) crontab = argv[1];
  618.  
  619.  
  620.   Undefine(entries);
  621.   entries = read_crontab();
  622.  
  623.   printf("-------------------------------------------------------------------------------\n");
  624.   printf("Events will be processed as necessary. You can go away now. To update the\n");
  625.   printf("CRON.TAB at any time, simply edit and save it. Cron will automatically load it.\n");
  626.   printf("-------------------------------------------------------------------------------\n");
  627.   update_list(entries);
  628.  
  629.  
  630.   while( 1 )
  631.     {
  632. // Get current time, determine how many seconds until the next minute.
  633. // Use 61 instead of 60 to make sure we have flipped over to the next
  634. // minute.
  635.       counter = 61 - localtime(time()).tm_sec;
  636.  
  637. // Wait one minute
  638.       while( counter>0 )
  639.         {
  640.           if( kbhit() )
  641.             switch( toupper(getch()) )
  642.               {
  643.               case 'Q': exit(0);
  644.               case 'E': invoke_editor(); break;
  645.               }
  646.           update_display();                             // Prints a timer.
  647.           Suspend(seconds * 1000);                      // wait some seconds.
  648.           counter -= seconds;                           // Note that we waited.
  649.         }
  650.  
  651.       if( cron_changed() )
  652.         {
  653.           printf("Crontab file has changed.\n");
  654.           Undefine(entries);
  655.           entries = read_crontab();
  656.           update_list(entries);
  657.         }
  658.  
  659.       cron_checks(entries);
  660.     }
  661. }
  662.